///////////////////////////////////////////////////////////////////////////////////////////////////
// Illusion Ray Development Kit (IDK)           Copyright (c) 2010 ( dimitriy.martynenko@gmail.com )
///////////////////////////////////////////////////////////////////////////////////////////////////
// Created : 2010-05-22
// Updated : ...
// Licence : NEW BSD
// Info	   : "SCL" Safe Code Lib (for C++ unmanaged)
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

/* M$ SPEC */
#	define CALLBACK									__stdcall
#	define WINAPI									__stdcall
#	define WINBASEAPI								extern __declspec(dllimport)
#	define WINUSERAPI								extern __declspec(dllimport)
#	define WINGDIAPI								extern __declspec(dllimport)
#	define OPENGLAPI								extern __declspec(dllimport)
#	define CONST const
#	define INVALID_HANDLE_VALUE						(Platform::Win32::HANDLE)(-1)
#	define PFD_DRAW_TO_WINDOW						4 
#	define PFD_DOUBLEBUFFER							1 
#	define PFD_SUPPORT_OPENGL						32 
#	define PFD_MAIN_PLANE							0 
#	define PFD_TYPE_RGBA							0

#	define PM_REMOVE								1

#	define MAX_PATH									260

/* WND MSG CMD */
#	define WM_CREATE								1
#	define WM_DESTROY								2
#	define SW_SHOW									5
#	define WM_SETFOCUS								7
#	define WM_KILLFOCUS								8
#	define WM_KEYDOWN								256
#	define WM_KEYUP									257
#	define WM_LBUTTONDOWN							513
#	define WM_LBUTTONUP								514
#	define WM_RBUTTONDOWN							516
#	define WM_RBUTTONUP								517
/* WND STYLE */
#	define WS_POPUP	0x80000000
#	define WS_VISIBLE	0x10000000

#define CS_VREDRAW          0x0001
#define CS_HREDRAW          0x0002

/*System Metrics*/
#	define SM_CXSCREEN 0
#	define SM_CYSCREEN 1
/* FILES */
#	define FILE_ATTRIBUTE_DIRECTORY		0x00000010
/* USER DATA*/
#	define GWL_USERDATA								(-21)

#	define TRUE 1
#	define FALSE 1

namespace Platform
{
	namespace Win32
	{
		/* M$ LIB */
#	pragma comment (lib, "kernel32.lib")
#	pragma comment (lib, "user32.lib")
#	pragma comment (lib, "gdi32.lib")
#	pragma comment (lib, "opengl32.lib")

		/* M$ TYPE */

		typedef const void *HANDLE;

		typedef unsigned long DWORD;
		typedef DWORD *PDWORD,*LPDWORD;

		typedef HANDLE HINSTANCE;

		typedef char *PCHAR,*LPCH,*PCH,*NPSTR,*LPSTR,*PSTR;
		typedef const char *LPCCH,*PCSTR,*LPCSTR;

		typedef int WINBOOL,*PWINBOOL,*LPWINBOOL;
		typedef WINBOOL BOOL;
		typedef unsigned char BYTE;

		typedef const void *PCVOID,*LPCVOID;
		typedef void *PVOID,*LPVOID;
		typedef char CHAR;
		typedef long LONG, *PLONG;
		typedef double LONGLONG,DWORDLONG;
		typedef int INT_PTR, *PINT_PTR;
		typedef unsigned int UINT_PTR, *PUINT_PTR;
		typedef long LONG_PTR, *PLONG_PTR;
		typedef unsigned long ULONG_PTR, *PULONG_PTR;
		typedef ULONG_PTR DWORD_PTR;
		typedef DWORD_PTR *PDWORD_PTR;
		typedef unsigned short UHALF_PTR, *PUHALF_PTR;
		typedef short HALF_PTR, *PHALF_PTR;
		typedef unsigned long HANDLE_PTR;
		typedef UINT_PTR WPARAM;
		typedef LONG_PTR LPARAM;
		typedef LONG_PTR LRESULT;
		typedef int INT;
		typedef unsigned int UINT,*PUINT,*LPUINT;
		typedef unsigned short WORD;
		typedef WORD ATOM;
		typedef HANDLE HWND;
		typedef HANDLE HDC;
		typedef HDC HGLRC;
		typedef __int64 INT64, *PINT64;
		typedef unsigned __int64 UINT64;

		typedef DWORD (WINAPI *LPTHREAD_START_ROUTINE)(void*);// TWEAK::
		typedef LRESULT(CALLBACK *WNDPROC)(HANDLE,UINT,WPARAM,LPARAM);

		typedef struct tagPIXELFORMATDESCRIPTOR {
			WORD nSize;
			WORD nVersion;
			DWORD dwFlags;
			BYTE iPixelType;
			BYTE cColorBits;
			BYTE cRedBits;
			BYTE cRedShift;
			BYTE cGreenBits;
			BYTE cGreenShift;
			BYTE cBlueBits;
			BYTE cBlueShift;
			BYTE cAlphaBits;
			BYTE cAlphaShift;
			BYTE cAccumBits;
			BYTE cAccumRedBits;
			BYTE cAccumGreenBits;
			BYTE cAccumBlueBits;
			BYTE cAccumAlphaBits;
			BYTE cDepthBits;
			BYTE cStencilBits;
			BYTE cAuxBuffers;
			BYTE iLayerType;
			BYTE bReserved;
			DWORD dwLayerMask;
			DWORD dwVisibleMask;
			DWORD dwDamageMask;
		} PIXELFORMATDESCRIPTOR,*PPIXELFORMATDESCRIPTOR,*LPPIXELFORMATDESCRIPTOR; 


		typedef struct _WNDCLASSEXA {
			UINT cbSize;
			UINT style;
			WNDPROC lpfnWndProc;
			int cbClsExtra;
			int cbWndExtra;
			HINSTANCE hInstance;
			HANDLE hIcon;
			HANDLE hCursor;
			HANDLE hbrBackground;
			LPCSTR lpszMenuName;
			LPCSTR lpszClassName;
			HANDLE hIconSm;
		} WNDCLASSEXA,*LPWNDCLASSEXA,*PWNDCLASSEXA;

		typedef struct tagPOINT {
			LONG x;
			LONG y;
		} POINT,POINTL,*PPOINT,*LPPOINT,*PPOINTL,*LPPOINTL;

		typedef struct tagMSG {
			HWND hwnd;
			UINT message;
			WPARAM wParam;
			LPARAM lParam;
			DWORD time;
			POINT pt;
		} MSG,*LPMSG,*PMSG;

		typedef union _LARGE_INTEGER {
			struct {
				DWORD LowPart;
				LONG HighPart;
			} c;
			struct {
				DWORD LowPart;
				LONG HighPart;
			} u;
			LONGLONG QuadPart;
		} LARGE_INTEGER, *PLARGE_INTEGER;

#	define STATUS_WAIT_0                    ((Platform::Win32::DWORD)0x00000000L)  
#	define IGNORE              0       // Ignore signal
#	define INFINITE            0xFFFFFFFF  // Infinite timeout
#	define WAIT_FAILED ((Platform::DWORD)0xFFFFFFFF)
#	define WAIT_OBJECT_0       ((STATUS_WAIT_0) + 0 )

		extern "C"
		{
			// kernel
			WINBASEAPI DWORD WINAPI GetModuleFileNameA(HINSTANCE,LPSTR,DWORD);
			WINBASEAPI HANDLE WINAPI LoadLibraryA(LPCSTR);
			WINBASEAPI HANDLE WINAPI GetProcAddress(HANDLE, LPCSTR);
			//WINBASEAPI LONG	WINAPI InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand);

			WINBASEAPI LONG WINAPI InterlockedExchange(LONG volatile *, LONG);
			WINBASEAPI LONG WINAPI InterlockedCompareExchange(LONG volatile *, LONG, LONG);

			WINBASEAPI DWORD WINAPI GetCurrentThreadId();
			WINBASEAPI LONG WINAPI InterlockedIncrement(LONG volatile *lpAddend);
			WINBASEAPI LONG WINAPI InterlockedDecrement(LONG volatile *lpAddend);

			WINBASEAPI HANDLE WINAPI CreateThread(HANDLE,DWORD,LPTHREAD_START_ROUTINE,PVOID,DWORD,PDWORD);
			WINBASEAPI BOOL WINAPI SetThreadPriority(HANDLE,INT);
			WINBASEAPI void WINAPI Sleep(DWORD);
			//inline void Sleep(DWORD)
			//{}

			WINBASEAPI HANDLE WINAPI CreateEventA(HANDLE, BOOL, BOOL, HANDLE);
			WINBASEAPI BOOL WINAPI SetEvent(HANDLE);
			WINBASEAPI BOOL WINAPI ResetEvent(HANDLE);
			WINBASEAPI DWORD WINAPI WaitForSingleObject(HANDLE, DWORD);
			WINBASEAPI DWORD WINAPI WaitForMultipleObjects(DWORD, const HANDLE*, BOOL, DWORD);


			WINBASEAPI void WINAPI OutputDebugStringA(LPCSTR lpOutputString);
			//inline void OutputDebugStringA(LPCSTR lpOutputString)
			//{}

			//file
			WINBASEAPI HANDLE WINAPI CreateFileA(LPCSTR,DWORD,DWORD,HANDLE,DWORD,DWORD,HANDLE);
			WINBASEAPI BOOL WINAPI CloseHandle(HANDLE);

			WINBASEAPI BOOL WINAPI WriteFile(HANDLE,PCVOID,DWORD,PDWORD,HANDLE);
			WINBASEAPI BOOL WINAPI ReadFile(HANDLE,PVOID,DWORD,PDWORD,HANDLE);

			WINBASEAPI DWORD WINAPI SetFilePointer(HANDLE,LONG,PLONG,DWORD);
			WINBASEAPI DWORD WINAPI GetFileSize(HANDLE,LPDWORD);


			// user32.lib
			WINUSERAPI LRESULT WINAPI DefWindowProcA(HANDLE,UINT,WPARAM,LPARAM);
			WINUSERAPI void WINAPI PostQuitMessage(INT);

			WINUSERAPI BOOL WINAPI	TranslateMessage(const MSG*);
			WINUSERAPI BOOL WINAPI	PeekMessageA(LPMSG,HWND,UINT,UINT,UINT);
			WINUSERAPI LONG WINAPI	DispatchMessageA(const MSG*);
			WINUSERAPI HDC	WINAPI	GetDC(HWND); 
			WINUSERAPI int	WINAPI	ReleaseDC(HWND, HDC);
			WINUSERAPI BOOL WINAPI  DestroyWindow(HWND);

			WINUSERAPI INT	WINAPI	GetSystemMetrics(INT);
			WINUSERAPI INT	WINAPI	ShowCursor(BOOL bShow);

			WINUSERAPI ATOM WINAPI RegisterClassExA(CONST WNDCLASSEXA*);
			WINUSERAPI HANDLE WINAPI CreateWindowExA(DWORD,LPCSTR,LPCSTR,DWORD,INT,INT,INT,INT,HWND,HANDLE,HINSTANCE,LPVOID);
			WINUSERAPI BOOL WINAPI ShowWindow(HWND,INT);
			WINUSERAPI BOOL WINAPI UpdateWindow(HWND);
			WINUSERAPI BOOL WINAPI SetForegroundWindow(HWND hWnd);
			
			WINUSERAPI LONG WINAPI SetWindowLongA(HWND,int,LONG);
			WINUSERAPI LONG WINAPI GetWindowLongA(HWND,int);
			//WINBASEAPI BOOL WINAPI QueryPerformanceCounter(PLARGE_INTEGER);
			//WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(PLARGE_INTEGER);
			//gdi.lib
			WINGDIAPI INT WINAPI ChoosePixelFormat(HDC,CONST PIXELFORMATDESCRIPTOR*); 
			WINGDIAPI BOOL WINAPI SetPixelFormat(HDC,INT,const PIXELFORMATDESCRIPTOR*);
			WINGDIAPI BOOL WINAPI SwapBuffers(HDC);

			//opengl32.lib
			OPENGLAPI HGLRC WINAPI wglCreateContext(HDC); 
			OPENGLAPI BOOL WINAPI wglMakeCurrent(HDC,HGLRC);
			OPENGLAPI BOOL WINAPI wglDeleteContext(HGLRC); 

			//HELPER
			WINBASEAPI BOOL WINAPI QueryPerformanceCounter(PLARGE_INTEGER);
			WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(PLARGE_INTEGER);

			WINBASEAPI DWORD WINAPI GetTickCount(void);
			WINBASEAPI HANDLE WINAPI GetCurrentProcess(void);
			WINBASEAPI HANDLE WINAPI GetCurrentThread(void);

			WINBASEAPI BOOL WINAPI GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask, PDWORD_PTR lpSystemAffinityMask);
			WINBASEAPI DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask);
		}
	}

	inline unsigned long Time::Get(void)
	{
		return Win32::GetTickCount();
	}

	inline void Time::Sleep(uint ms)
	{
		Win32::Sleep(ms);
	}

	//
	// OutputConsole Win32 Impl
	//
	inline void OutputConsole::Message(const char* text)
	{
		Platform::Win32::OutputDebugStringA(text);
	}

	//
	// FileHandle
	//
	class FileHandle
	{
	private:
		FileHandle(const FileHandle&);
		FileHandle& operator=(const FileHandle&);
	private:
		Win32::HANDLE mHandle;
	public:
		FileHandle()
			: mHandle(INVALID_HANDLE_VALUE)
		{
		}

		bool IsValid() const
		{
			return mHandle != INVALID_HANDLE_VALUE;
		}

		bool Open(const char* path, bool create)
		{
			const dword FILE_ATTRIB_NORMAL = 0x0000080;
			//
			const dword GENERIC_R = 0x80000000 ;
			const dword GENERIC_W = 0x40000000; // TODO: in mode?
			const dword GENERIC = (create) ? GENERIC_W : GENERIC_R;

			const dword OPEN_EXISTING = 3;
			const dword CREATE_ALWAYS = 2;
			const dword MODE = (create) ? CREATE_ALWAYS : OPEN_EXISTING;

			mHandle = Win32::CreateFileA(path, GENERIC, 0, 0, MODE, FILE_ATTRIB_NORMAL, 0);

			return mHandle != INVALID_HANDLE_VALUE;
		}

		void Close()
		{
			if(IsValid())
			{
				Win32::CloseHandle(mHandle);
			}

			mHandle = INVALID_HANDLE_VALUE;
		}

		uint GetSize() const
		{
			return Win32::GetFileSize(mHandle, 0);
		}

		void SetPosition(uint pos)
		{
			Win32::SetFilePointer(mHandle, pos, 0, 0);
		}

		void Seek(uint pos)
		{
			Win32::SetFilePointer(mHandle, pos, 0, 1);
		}

		template<typename T>
		bool Read(T data, uint size, dword& bread)
		{
			return Win32::ReadFile(mHandle, data, size, &bread, 0) != FALSE;
		}
	};

	//
	// TRefCounter Win32 Impl
	//
	//NOTE : Win32 will be use Interlocked...Acquire when perfomance is critical?

	template<>
	inline void TRefCounter<Config::ThreadSafe>::AddRef(void)
	{
		Win32::InterlockedIncrement(&mRefCount);
	}

	template<>
	inline bool TRefCounter<Config::ThreadSafe>::Release()
	{
		return Win32::InterlockedDecrement(&mRefCount) == 0;
	}

	//
	// TThreadLocker Win32 Impl
	//
	class Locker
	{
	private:
		Locker(const Locker&);
		Locker& operator=(const Locker&);
	private:
		volatile long mThreadID;
	public:
		inline Locker() : mThreadID(0) {}

		volatile inline bool TryLock(void)
		{
			return Win32::InterlockedCompareExchange(&mThreadID, Win32::GetCurrentThreadId(), 0) != 0;
		}

		volatile inline void UnLock(void)
		{
			Win32::InterlockedExchange(&mThreadID, 0);
		}
	};

	//
	// TMutex Win32 Impl
	//
	template<>
	class TMutex<Config::ThreadSafe>
	{
	private:
		Locker& mLocker;
	private:
		TMutex(const TMutex&);
		TMutex& operator=(const TMutex&);
	private:
		inline void Enter()
		{
			for(int i = 0; mLocker.TryLock(); ++i)
			{
				if(i == Platform::Config::ThreadLockMaxTimeIterationToSleep)
				{
					i = 0;
					Win32::Sleep(Platform::Config::ThreadLockSleepTimeInMs);
				}
			}
		}

		inline void Leave()
		{
			mLocker.UnLock();
		}
	public:
		inline TMutex(Locker& locker) : mLocker(locker)
		{
			Enter();
		}

		~TMutex()
		{ 
			Leave();
		}
	};

	//
	// Event Win32 Impl
	//
	class Event
	{
	public:
		Win32::HANDLE mHandle;
	private:
		Event(const Event&);
		Event& operator=(const Event&);
	public:
		Event(bool autoReset)
		{
			mHandle = Win32::CreateEventA(0, autoReset, 0, 0);
		}

		~Event()
		{
			Win32::CloseHandle(mHandle);
		}

		void Set()
		{
			Win32::SetEvent(mHandle);
		}

		void Reset()
		{
			Win32::ResetEvent(mHandle);
		}
	};

	//
	// TEventCondition Win32 Impl
	//

	class EventCondition 
	{
	private:
		EventCondition(const EventCondition&);
		EventCondition& operator=(const EventCondition&);
	public:
		typedef unsigned int EventID;

		EventCondition(){}

		bool Wait(Event& event1)
		{
			return WAIT_OBJECT_0 != Win32::WaitForSingleObject(event1.mHandle, INFINITE);
		}

		EventID Wait(Event& event1, Event& event2)
		{
			Win32::HANDLE waitEvents[2] = {event1.mHandle, event2.mHandle};
			return Win32::WaitForMultipleObjects(2, waitEvents, 0, INFINITE);
		}
	};

	//
	// Thread Win32 Impl
	//

	class ThreadHandle
	{
	private:
		ThreadHandle(const ThreadHandle&);
		ThreadHandle& operator=(const ThreadHandle&);
	private:
		Win32::HANDLE mHandle;
		Win32::DWORD mThreadID;
	private:
		template<typename Derived, void (Derived::*action)()>
		static Win32::DWORD WINAPI __ThreadCall(void* p)
		{
			if(!p) return 0;

			((*(Derived*)p).*action)();

			return 0;
		}
	public:
		ThreadHandle(){}

		template<typename Derived, void (Derived::*action)()>
		void Create(Derived* caller)
		{
			mHandle = Win32::CreateThread(0, 0, __ThreadCall<Derived, action>, caller, 0, &mThreadID);
		}

		void Close()
		{
			Win32::CloseHandle(mHandle);
		}
	};

	//
	// Window Impl
	// 
	class WindowHandle
	{
	protected:
		bool mActive;
		bool mAlive;
		Win32::HWND mHWND;
		Win32::HDC mHDC;

		uint mLeft;
		uint mTop;
		uint mWidth;
		uint mHeight;
	private:
		static Win32::LRESULT CALLBACK __WindowProc(Win32::HANDLE hWnd, Win32::UINT message, Win32::WPARAM wParam, Win32::LPARAM lParam)
		{
			WindowHandle* window = (WindowHandle*)Win32::GetWindowLongA(hWnd, GWL_USERDATA);

			if(window)
			{
				switch(message)
				{
				case WM_LBUTTONDOWN:
					window->Down(InputSystem::MouseEvent(InputSystem::MouseCode::Left, lParam & 0xFFFF, lParam >> 16));
					break;

				case WM_RBUTTONDOWN:
					window->Down(InputSystem::MouseEvent(InputSystem::MouseCode::Right, lParam & 0xFFFF, lParam >> 16));
					break;

				case WM_LBUTTONUP:
					window->Up(InputSystem::MouseEvent(InputSystem::MouseCode::Left, lParam & 0xFFFF, lParam >> 16));
					break;

				case WM_RBUTTONUP:
					window->Up(InputSystem::MouseEvent(InputSystem::MouseCode::Right, lParam & 0xFFFF, lParam >> 16));
					break;

				case WM_DESTROY:
					window->Destroy();
					break;

				case WM_SETFOCUS:
					window->Active(true);
					break;

				case WM_KILLFOCUS:
					window->Active(false);
					break;

				case WM_KEYDOWN:
					if ((lParam & 0x40000000) != 0x40000000) //ignore autorepeat
					{
						window->Down(InputSystem::KeyboardEvent(wParam));
					}
					break;

				case WM_KEYUP:
					window->Up(InputSystem::KeyboardEvent(wParam));
					break;
				}
			}
			return Win32::DefWindowProcA(hWnd, message, wParam, lParam);
		}
	public:
		typedef Win32::HDC HandleType;

		WindowHandle() 
			: mHWND(INVALID_HANDLE_VALUE),
			mHDC(INVALID_HANDLE_VALUE)
		{
		}

		virtual ~WindowHandle()
		{
			Close();
		}

		virtual void Down(const InputSystem::MouseEvent& code) = 0;
		virtual void Up(const InputSystem::MouseEvent& code) = 0;
		virtual void Down(const InputSystem::KeyboardEvent& code) = 0;
		virtual void Up(const InputSystem::KeyboardEvent& code) = 0;

		void Destroy()
		{
			mAlive = false;
		}

		bool IsAlive() const
		{
			return mAlive;
		}

		void Active(bool mode)
		{
			if (mAlive)
			{
				mActive = mode;
			}
		}

		bool IsActive() const
		{
			return mActive;
		}

		bool Create()
		{
			Win32::WNDCLASSEXA wndClass = { 0 };

			wndClass.cbSize = sizeof(Win32::WNDCLASSEXA);
			wndClass.style = CS_HREDRAW | CS_VREDRAW;
			wndClass.lpfnWndProc = __WindowProc;
			wndClass.lpszClassName = "IDKWND";

			Win32::RegisterClassExA(&wndClass);

			mHWND = Win32::CreateWindowExA(
				0,
				"IDKWND",
				"IDK",
				//WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
				WS_POPUP | WS_VISIBLE,
				mLeft,
				mTop,
				mWidth,
				mHeight,
				0, 0, 0, 0);

			Win32::SetWindowLongA(mHWND, GWL_USERDATA, (Win32::LONG)this);

			mHDC = Win32::GetDC(mHWND);

			Win32::ShowWindow(mHWND, SW_SHOW);
			Win32::UpdateWindow(mHWND);
			Win32::SetForegroundWindow(mHWND);
			//Win32::ShowCursor(false);

			//get device context

			mActive = true;

			return mAlive = true;
		}

		void PeekMessages()
		{
			Win32::MSG msg = {0, 0, 0, 0, 0, 0, 0};
			while(Win32::PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
			{
				Win32::TranslateMessage(&msg);
				Win32::DispatchMessageA(&msg);
			}
		}

		void Close()
		{
			if(mHDC != INVALID_HANDLE_VALUE)
			{
				Win32::ReleaseDC(mHWND, mHDC);
				Win32::DestroyWindow(mHWND);
			}
		}

		HandleType GetHWND() const
		{
			return mHWND;
		}

		HandleType GetDC() const
		{
			return mHDC;
		}
	};


	template<typename T>
	inline T Math<T>::Sqrt(T x)
	{
		__asm
		{
			fld x // r0 = x
				fsqrt // r0 = sqrtf( r0 )
		}// returns r0
	}

	template<typename T>
	inline T Math<T>::Sin(T x)
	{
		T r = (T)0;

		__asm
		{
			fld x
				fsin
				fstp r
		}

		return r;
	}

	template<typename T>
	inline T Math<T>::Cos(T x)
	{
		T r = (T)0;

		__asm
		{
			fld x
				fcos
				fstp r
		}

		return r;
	}

	template<typename T>
	inline T Math<T>::ArcTan2(T y, T x)
	{
		__asm
		{
			fld y
				fld x
				fpatan // r0 = atan2(y/x)
		} // returns r0
	}
}
